package com.kinsin.kinsinlogsystem;

import android.os.Build;
import android.os.Process;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 描述：Mmap映射日志写入
 * 作者：Kinsin
 * <p>
 * 使用方法：
 * 1、创建一个KinsinMMapLogWriter对象，指定文件路径和编码格式
 * 2、调用writeLog方法写入日志
 */
public class KinsinMMapLogWriter {
    private static final String TAG = "TextMMapWriter";
    private static final int INIT_FILE_SIZE = 2 * 1024 * 1024;  // 初始2MB
    private static final int MAX_BUFFER_ITEMS = 1000;

    private final ReentrantLock fileLock = new ReentrantLock();
    private final AtomicBoolean isRunning = new AtomicBoolean(false);
    private final ConcurrentLinkedQueue<String> bufferQueue = new ConcurrentLinkedQueue<>();

    private RandomAccessFile randomAccessFile;
    private FileChannel fileChannel;
    private MappedByteBuffer mappedBuffer;
    private final File logFile;
    private final String charsetName;

    private int currentWritePos = 0;
    private int mappedFileSize = INIT_FILE_SIZE;

    private final ConcurrentLinkedQueue<WriteCallback> callbacks = new ConcurrentLinkedQueue<>();

    // 构造函数指定编码

    /**
     * 构造函数
     *
     * @param filePath    文件路径
     * @param charsetName 字符编码如："UTF-8"
     * @throws IOException
     */
    public KinsinMMapLogWriter(String filePath, String charsetName) throws IOException {
        if (filePath == null || filePath.isEmpty()) {
            throw new IllegalArgumentException("File path cannot be null or empty.");
        }
        if (charsetName == null || charsetName.isEmpty()) {
            throw new IllegalArgumentException("Charset name cannot be null or empty.");
        }
        this.logFile = new File(filePath);
        this.charsetName = charsetName;
        initializeTextFile();
        setupMemoryMapping();
        startConsumer();
    }

    // region 初始化
    private void initializeTextFile() throws IOException {
        if (!logFile.exists()) {
            File parent = logFile.getParentFile();
            if (parent != null && !parent.exists() && !parent.mkdirs()) {
                throw new IOException("Create directories failed: " + parent);
            }

            FileUtils.createFile(logFile.getAbsolutePath());
            // 创建新文件时添加UTF-8 BOM（可选）
//            try (RandomAccessFile raf = new RandomAccessFile(logFile, "rw")) {
////                if ("UTF-8".equalsIgnoreCase(charsetName)) {
////                    raf.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
////                    currentWritePos = 3; // BOM占3字节
////                }
//                raf.setLength(INIT_FILE_SIZE);
//            }
        }
    }


    private void setupMemoryMapping() throws IOException {
        try {
            randomAccessFile = new RandomAccessFile(logFile, "rw");
            fileChannel = randomAccessFile.getChannel();

            // 使用文件实际大小初始化写入位置
            currentWritePos = (int) randomAccessFile.length();
            mappedFileSize = Math.max(INIT_FILE_SIZE, currentWritePos + 1);
            randomAccessFile.setLength(mappedFileSize); // 确保文件足够大

            mappedBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, mappedFileSize);
            mappedBuffer.position(currentWritePos); // 定位到文件末尾

            Log.d(TAG, "Initial write position: " + currentWritePos);
        } catch (IOException e) {
            closeResources();
            throw new IOException("Memory mapping failed: " + e.getMessage());
        }
    }

//    private void setupMemoryMapping() throws IOException {
//        try {
//            randomAccessFile = new RandomAccessFile(logFile, "rw");
//            fileChannel = randomAccessFile.getChannel();
//            mappedBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, INIT_FILE_SIZE);
//
//            // 定位到文件实际末尾
//            try (FileInputStream fis = new FileInputStream(logFile)) {
//                byte[] buffer = new byte[1024];
//                int read;
//                while ((read = fis.read(buffer)) != -1) {
//                    for (int i = 0; i < read; i++) {
//                        if (buffer[i] == 0) {
//                            currentWritePos = i;
//                            break;
//                        }
//                    }
//                }
//            }
//            Log.d(TAG, "Initial write position: " + currentWritePos);
//        } catch (IOException e) {
//            closeResources();
//            throw new IOException("Memory mapping failed: " + e.getMessage());
//        }
//    }
    // endregion

    // region 核心逻辑
    public void writeLog(String log) {
        bufferQueue.offer(log + "\n"); // 统一换行符为\n
        if (bufferQueue.size() > MAX_BUFFER_ITEMS / 2) {
            synchronized (bufferQueue) {
                bufferQueue.notify();
            }
        }
    }

    public void writeLog(String log, WriteCallback callback) {
        bufferQueue.offer(log + "\n");
        callbacks.offer(callback);
        if (bufferQueue.size() > MAX_BUFFER_ITEMS / 2) {
            synchronized (bufferQueue) {
                bufferQueue.notify();
            }
        }
    }

    /**
     * 同步写入
     * @param log
     * @throws IOException
     */
    public synchronized void writeLogSync(String log) throws IOException {
        bufferQueue.offer(log + "\n"); // 统一换行符为\n
        processBuffer();
    }

    private void startConsumer() {
        isRunning.set(true);
        Thread worker = new Thread(new Runnable() {
            @Override
            public void run() {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                while (isRunning.get() || !bufferQueue.isEmpty()) {
                    try {
                        processBuffer();
                        Thread.sleep(50);
                    } catch (Exception e) {
                        Log.e(TAG, "Consumer error: " + e.getMessage());
                    }
                }
            }
        }, "MMap-Consumer");
        worker.start();
    }

    private void processBuffer() throws IOException {
        fileLock.lock();
        try {
            while (!bufferQueue.isEmpty()) {
                String log = bufferQueue.poll();
                byte[] data = log.getBytes(charsetName); // 按指定编码转换

                if (currentWritePos + data.length > mappedFileSize) {
                    expandMapping(mappedFileSize, data.length);
                }

                mappedBuffer.position(currentWritePos);
                mappedBuffer.put(data);
                currentWritePos += data.length;
            }
            mappedBuffer.force();
            while (!callbacks.isEmpty()) {
                callbacks.poll().onWriteComplete(); // 触发回调
            }
        } finally {
            fileLock.unlock();
        }
    }
    // endregion

    // region 内存管理
    private void expandMapping(int requiredSize, int nextWriteSize) throws IOException {
        Log.d(TAG, "expandMapping: requiredSize = " + requiredSize);
        fileLock.lock();
        try {
            // 新的文件大小，扩展2MB,如果当前文件是10MB，则只扩展16字节
            int newSize = requiredSize;
            if (requiredSize >= 10 * 1024 * 1024) {
                newSize = requiredSize + nextWriteSize;
                Log.d(TAG, "expandMapping: 执行+nextWriteSize: " + nextWriteSize + "， newSize = " + newSize);
            } else {
                newSize = requiredSize + 2 * 1024 * 1024;
                Log.d(TAG, "expandMapping: 执行+2MB， newSize = " + newSize);
            }

            // 物理扩展文件
            randomAccessFile.setLength(newSize);
            fileChannel = randomAccessFile.getChannel();

            // 重新映射
            mappedBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, newSize);
            mappedFileSize = newSize;
        } finally {
            fileLock.unlock();
        }
    }
    // endregion

    // region 资源清理
    public void close() {
        isRunning.set(false);

        // 确保消费线程退出
        synchronized (bufferQueue) {
            bufferQueue.notifyAll();
        }

        fileLock.lock(); // 加锁保证最后操作原子性
        try {
            // 处理剩余数据
            int retry = 3;
            while (retry-- > 0 && !bufferQueue.isEmpty()) {
                try {
                    processBuffer();
                    Thread.sleep(50);
                } catch (Exception e) {
                    Log.e(TAG, "Final flush error: " + e.getMessage());
                }
            }

            // 强制刷盘
            if (mappedBuffer != null) {
                mappedBuffer.force();
            }

            // 关闭资源
            closeResources();

            // 直接使用File进行截断操作
            if (logFile.exists()) {
                try (FileOutputStream fos = new FileOutputStream(logFile, true)) {
                    FileChannel fc = fos.getChannel();
                    fc.truncate(currentWritePos);
                    Log.d(TAG, "Successfully truncated to: " + currentWritePos);
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Close error: " + e.getMessage());
        } finally {
            fileLock.unlock();
        }
    }

    // 修改后的资源关闭方法
    private void closeResources() {
        try {
            cleanMappedBuffer();
            if (fileChannel != null && fileChannel.isOpen()) {
                fileChannel.close();
            }
            if (randomAccessFile != null) {
                randomAccessFile.close();
            }
        } catch (IOException e) {
            Log.e(TAG, "Resource cleanup failed: " + e.getMessage());
        }
    }

    //    public void close() {
//        isRunning.set(false);
//        try {
//            Thread.sleep(200);
//            processBuffer(); // 确保处理剩余数据
//            if (mappedBuffer != null) {
//                mappedBuffer.force(); // 强制刷盘
//            }
//            closeResources(); // 先释放资源
//            truncateFileAfterClose(); // 重新打开并截断
//        } catch (Exception e) {
//            Log.e(TAG, "Close error: " + e.getMessage());
//        }
//    }

//    private void truncateFileAfterClose() {
//        fileLock.lock();
//        try (RandomAccessFile raf = new RandomAccessFile(logFile, "rw")) {
//            raf.setLength(currentWritePos); // 安全截断
//            Log.d(TAG, "Truncated to actual size: " + currentWritePos);
//        } catch (IOException e) {
//            Log.e(TAG, "Truncate after close failed: " + e.getMessage());
//        } finally {
//            fileLock.unlock();
//        }
//    }


//    public void close() {
//        isRunning.set(false);
//        try {
//            Thread.sleep(200);
//            processBuffer();
//            truncateFile(); // 最终文件截断
//        } catch (Exception e) {
//            Log.e(TAG, "Close error: " + e.getMessage());
//        } finally {
//            closeResources();
//        }
//    }
//
//    private void truncateFile() {
//        fileLock.lock();
//        try {
//            // 截断到实际写入位置
//            randomAccessFile.setLength(currentWritePos);
//            Log.d(TAG, "Truncated to actual size: " + currentWritePos);
//        } catch (IOException e) {
//            Log.e(TAG, "Truncate failed: " + e.getMessage());
//        } finally {
//            fileLock.unlock();
//        }
//    }

//    private void closeResources() {
//        fileLock.lock();
//        try {
//            cleanMappedBuffer();
//            if (fileChannel != null) fileChannel.close();
//            if (randomAccessFile != null) randomAccessFile.close();
//        } catch (IOException e) {
//            Log.e(TAG, "Resource cleanup failed: " + e.getMessage());
//        } finally {
//            fileLock.unlock();
//        }
//    }

    @SuppressWarnings("all")
    private void cleanMappedBuffer() {
        try {
            if (mappedBuffer == null) return;

            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
                Method freeMethod = mappedBuffer.getClass().getDeclaredMethod("free");
                freeMethod.setAccessible(true);
                freeMethod.invoke(mappedBuffer);
            } else {
                Method cleanerMethod = mappedBuffer.getClass().getMethod("cleaner");
                cleanerMethod.setAccessible(true);
                Object cleaner = cleanerMethod.invoke(mappedBuffer);
                if (cleaner != null) {
                    Class<?> cleanerClass = Class.forName("sun.misc.Cleaner");
                    Method cleanMethod = cleanerClass.getMethod("clean");
                    cleanMethod.invoke(cleaner);
                }
            }
        } catch (Exception e) {
            Log.w(TAG, "Clean buffer failed: " + e.getMessage());
        }
    }
    // endregion

    public interface WriteCallback {
        void onWriteComplete();
    }

}